`include "defines.v"
module ex(
	input wire rst,
	input wire[31:0] reg1_i,
	input wire[31:0] reg2_i,
	input wire[4:0] reg3_addr_i,
	input wire reg3_write_i,
	input wire[`AluOpWidth-1:0] alu_op_i,
	// input wire[`AluSelWidth-1:0] alu_sel_i,
	
	output reg[31:0] write_data_o,
	output reg write_ce_o,
	output reg[4:0] write_addr_o,
	
	//传给id模块的将要写的数据
	output wire[4:0] ex_id_write_addr_o,
	output wire ex_id_write_ce_o,
	output wire[31:0] ex_id_write_data_o,
	//与hilo寄存器相关的信号
	input wire[31:0] hi_i,
	input wire[31:0] lo_i,
	
	input wire mem_ex_hilo_write_ce_i,
	input wire[31:0] mem_ex_hi_i,
	input wire[31:0] mem_ex_lo_i,
	
	input wire wb_ex_hilo_write_ce_i,
	input wire[31:0] wb_ex_hi_i,
	input wire[31:0] wb_ex_lo_i,
	
	//传给hilo寄存器要写的数0据
	output reg[31:0] hi_o,
	output reg[31:0] lo_o,
	output reg hilo_write_ce_o,
	//控制流水线暂停信号
	output wire pause_o,
	input wire[63:0] hilo_temp_i, //第一个执行周期的乘法结果
	input wire[1:0] count_i, //当前处于第几个执行周期
	output reg[63:0] hilo_temp_o, //第一个执行周期的乘法结果
	output reg[1:0] count_o, //下一时钟周期处于执行阶段的第几个周期
	
	//除法相关信号
	input wire div_finish_i,
	input wire[63:0] div_result_i,
	output reg[31:0] div_data1_o,
	output reg[31:0] div_data2_o,
	output reg div_signed_o,
	output reg div_start_o
);
	integer i;
	integer isFind; //clo,clz指令标志位
	reg[31:0] HI;//保存HI的最新值
	reg[31:0] LO;//保存LO的最新值
	wire OF; //溢出标志
	wire reg1_lt_reg2; //reg1是否小于reg2? 1 : 0
	wire[31:0] reg2_i_vc; //进行减法或有符号比较运算时，
						  //为reg2_i的变补（variable compensation）结果， a[补]的变补=-a[补]，
						  //否则为reg2_i本身
	wire[31:0] sum; //保存加减法结果,减法运算用加法代替
	wire[31:0] mult1; //乘数1
	wire[31:0] mult2; //乘数2
	wire[63:0] multu_result; //无符号相乘结果
	reg[63:0] mult_result; //最终的符号修正结果
	reg[63:0] madd_msub_result; //乘累加，乘累减结果
	
	reg msub_madd_pause; //乘累加，乘累减暂停信号
	reg div_pause; //除法暂停信号
	
	assign pause_o = msub_madd_pause | div_pause; //流水线暂停信号
	//变补或本身
	assign reg2_i_vc = (alu_op_i == `ALU_SUB_OP || 
				alu_op_i == `ALU_SUBU_OP ||
				alu_op_i == `ALU_SLT_OP)? 
				(~reg2_i + 1) : reg2_i;
	//加减法结果
	assign sum = reg1_i + reg2_i_vc;
	//溢出标志
	assign OF = (reg1_i[31] == reg2_i_vc[31] && sum[31] != reg2_i_vc[31]);
	//reg1是否小于reg2
	assign reg1_lt_reg2 = (alu_op_i == `ALU_SLT_OP)?
						  ((reg1_i[31] && !reg2_i[31]) ||  //reg1为负数,reg2为正数
						   (!reg1_i[31] && !reg2_i[31] && sum[31]) ||  //reg1为正数,reg2为正数，相减结果为负数
						   (reg1_i[31] && reg2_i[31] && sum[31])):   //reg1为负数,reg2为负数，相减结果为负数
						  (reg1_i < reg2_i);
	
	assign mult1 = ((alu_op_i == `ALU_MULT_OP ||
					alu_op_i == `ALU_MUL_OP ||
					alu_op_i == `ALU_MADD_OP ||
					alu_op_i == `ALU_MSUB_OP) && reg1_i[31])? //如果进行有符号乘法，转为无符号乘法，符号另外处理
					(~reg1_i + 1) : reg1_i;
					
	assign mult2 = ((alu_op_i == `ALU_MULT_OP || 
					alu_op_i == `ALU_MUL_OP ||
					alu_op_i == `ALU_MADD_OP ||
					alu_op_i == `ALU_MSUB_OP) && reg2_i[31])? //如果进行有符号乘法，转为无符号乘法，符号另外处理
					(~reg2_i + 1) : reg2_i;
	assign multu_result = mult1 * mult2; //无符号乘法运算结果
	//处理符号位
	always@(*)
		if((alu_op_i == `ALU_MUL_OP || 
			alu_op_i == `ALU_MULT_OP ||
			alu_op_i == `ALU_MADD_OP ||
			alu_op_i == `ALU_MSUB_OP) && reg1_i[31] != reg2_i[31]) //符号不一样，调整结果为负数
			mult_result = (~multu_result + 1);
		else
			mult_result = multu_result;

			
	//处理hilo_temp_o
	always@(*)
		if(rst == `RstEnable)
			hilo_temp_o = {`ZeroWord, `ZeroWord};
		else if(alu_op_i == `ALU_MADD_OP || alu_op_i == `ALU_MADDU_OP)
			hilo_temp_o = mult_result;
		else if(alu_op_i == `ALU_MSUB_OP || alu_op_i == `ALU_MSUBU_OP)
			hilo_temp_o = ~mult_result + 1;
		else
			hilo_temp_o = {`ZeroWord, `ZeroWord};

	//乘累加，累减暂停信号， count_o信号
	always@(*)
		if(rst == `RstEnable)
		begin
			msub_madd_pause = `NO_PAUSE;
			count_o = 2'b00;
		end
		else if(alu_op_i == `ALU_MADD_OP || 
			alu_op_i == `ALU_MSUB_OP || 
			alu_op_i == `ALU_MADDU_OP ||
			alu_op_i == `ALU_MSUBU_OP)
			if(count_i == 2'b00) //乘累加，乘累减第一个周期
			begin
				msub_madd_pause = `PAUSE;
				count_o = 2'b01;
			end
			else if(count_i == 2'b01) //乘累加，乘累减第二个周期
			begin
				msub_madd_pause = `NO_PAUSE;
				count_o = 2'b10;
			end
			else
			begin
				msub_madd_pause = `NO_PAUSE;
				count_o = 2'b00;
			end	
		else
		begin
			msub_madd_pause = `NO_PAUSE;
			count_o = 2'b00;
		end	
	//处理乘累加，乘累减结果
	always@(*)
		if(count_i == 2'b01) 
			if(alu_op_i == `ALU_MADDU_OP || 
				alu_op_i == `ALU_MADD_OP || 
				alu_op_i == `ALU_MSUB_OP || 
				alu_op_i == `ALU_MSUBU_OP)
				madd_msub_result = {HI, LO} + hilo_temp_i;
			else 
				madd_msub_result = {`ZeroWord, `ZeroWord};
		else
			madd_msub_result = {`ZeroWord, `ZeroWord};
	
	//处理除法暂停，除数，被除数，有符号，开始除法信号
	always@(*)
		if(rst == `RstEnable)
		begin
			div_pause = `NO_PAUSE;
			div_signed_o = 1'b0; 
			div_data1_o = `ZeroWord;
			div_data2_o = `ZeroWord;
			div_start_o = `DivStop;
		end
		else if(alu_op_i == `ALU_DIV_OP || alu_op_i == `ALU_DIVU_OP)
		begin
			if(div_finish_i == 1'b0)
			begin
				div_pause = `PAUSE;
				div_signed_o = (alu_op_i == `ALU_DIV_OP)? 1'b1 : 1'b0;
				div_data1_o = reg1_i;
				div_data2_o = reg2_i;
				div_start_o = `DivStart;
			end
			else 
			begin
				div_pause = `NO_PAUSE;
				div_signed_o = 1'b0;
				div_data1_o = `ZeroWord;
				div_data2_o = `ZeroWord;
				div_start_o = `DivStop;
			end
		end
		else
		begin
			div_pause = `NO_PAUSE;
			div_signed_o = 1'b0;
			div_data1_o = `ZeroWord;
			div_data2_o = `ZeroWord;
			div_start_o = `DivStop;
		end
	always@(*)
		if(rst == `RstEnable)
		begin
			write_data_o = `ZeroWord;
			write_ce_o = `WriteDisable;
			write_addr_o = 5'b00000;
		end
		else
		begin
			write_ce_o = reg3_write_i;
			write_addr_o = reg3_addr_i;
			case (alu_op_i)
				`ALU_OR_OP:
				begin
					write_data_o = reg1_i | reg2_i;
				end
				`ALU_AND_OP:
				begin
					write_data_o = reg1_i & reg2_i;
				end
				`ALU_XOR_OP:
				begin
					write_data_o = reg1_i ^ reg2_i;
				end
				`ALU_NOR_OP:
				begin
					write_data_o = ~(reg1_i | reg2_i);
				end
				`ALU_LUI_OP:
				begin
					write_data_o = reg1_i;
				end
				`ALU_SLL_OP:
				begin
					write_data_o = reg2_i << reg1_i[4:0];
				end
				`ALU_SRL_OP:
				begin
					write_data_o = reg2_i >> reg1_i[4:0];
				end
				`ALU_SRA_OP:
				begin
					write_data_o = ({32{reg2_i[31]}} << (32 - reg1_i[4:0])) | (reg2_i >> reg1_i[4:0]);
				end
				`ALU_MOVZ_OP:
				begin
					write_data_o = reg1_i;
				end
				`ALU_MOVN_OP:
				begin
					write_data_o = reg1_i;
				end
				`ALU_MFHI_OP:
				begin
					write_data_o = HI;
				end
				`ALU_MFLO_OP:
				begin
					write_data_o = LO;
				end
				`ALU_SLT_OP, `ALU_SLTU_OP:
				begin
					write_data_o = reg1_lt_reg2;
				end
				`ALU_ADD_OP:
				begin
					write_data_o = sum;
					if(OF) write_ce_o = `WriteDisable;
				end
				`ALU_ADDU_OP:
				begin
					write_data_o = sum;
				end
				`ALU_SUB_OP:
				begin
					write_data_o = sum;
						if(OF) write_ce_o = `WriteDisable;
				end
				`ALU_SUBU_OP:
				begin
					write_data_o = sum;
				end
				`ALU_CLZ_OP:
				begin
					write_data_o = 32;
					isFind = 0;
					for(i = 31; i >= 0; i = i - 1)
						if(!isFind && reg1_i[i] == 1 ) 
						begin
							write_data_o = 31 - i;
							isFind = 1;
						end
				end
				`ALU_CLO_OP:
				begin
					write_data_o = 32;
					isFind = 0;
					for(i = 31; i >= 0; i = i - 1)
						if(!isFind && reg1_i[i] == 0) 
						begin
							write_data_o = 31 - i;
							isFind = 1;
						end
				end
				`ALU_MUL_OP:
				begin
					write_data_o = mult_result[31:0];
				end
				default:
				begin 
					write_data_o = `ZeroWord;
				end
			endcase
			
		end
		
	assign ex_id_write_addr_o = write_addr_o; 
	assign ex_id_write_ce_o = write_ce_o;
	assign ex_id_write_data_o = write_data_o;
	
	//更新HI LO为最新值
	always@(*)
		if(rst == `RstEnable)
			{HI, LO} = {`ZeroWord, `ZeroWord};
		else if(mem_ex_hilo_write_ce_i == `WriteEnable)
			{HI, LO} = {mem_ex_hi_i, mem_ex_lo_i};
		else if(wb_ex_hilo_write_ce_i == `WriteEnable)
			{HI, LO} = {wb_ex_hi_i, wb_ex_lo_i};
		else 
			{HI, LO} = {hi_i, lo_i};
	//给hi lo 寄存器赋值逻辑
	always@(*)
		if(rst == `RstEnable)
		begin
			hi_o = `ZeroWord;
			lo_o = `ZeroWord;
			hilo_write_ce_o = `WriteDisable;
		end
		else if(alu_op_i == `ALU_MTHI_OP)
		begin
			hi_o = reg1_i;
			lo_o = LO;
			hilo_write_ce_o = `WriteEnable;
		end
		else if(alu_op_i == `ALU_MTLO_OP)
		begin
			hi_o = HI;
			lo_o = reg1_i;
			hilo_write_ce_o = `WriteEnable;
		end
		//乘法操作结果
		else if(alu_op_i == `ALU_MULT_OP || alu_op_i == `ALU_MULTU_OP)
		begin
			hi_o = mult_result[63:32];
			lo_o = mult_result[31:0];
			hilo_write_ce_o = `WriteEnable;
		end
		//乘累加，乘累减结果
		else if((alu_op_i == `ALU_MADD_OP ||
				alu_op_i == `ALU_MADDU_OP ||
				alu_op_i == `ALU_MSUB_OP ||
				alu_op_i == `ALU_MSUBU_OP) && count_i == 2'b01)
		begin
			hi_o = madd_msub_result[63:32];
			lo_o = madd_msub_result[31:0];
			hilo_write_ce_o = `WriteEnable;
		end
		else if((alu_op_i == `ALU_DIV_OP || alu_op_i == `ALU_DIVU_OP) && div_finish_i == 1'b1)
		begin
			hi_o = div_result_i[63:32];
			lo_o = div_result_i[31:0];
			hilo_write_ce_o = `WriteEnable;
		end
		else
		begin
			hi_o = `ZeroWord;
			lo_o = `ZeroWord;
			hilo_write_ce_o = `WriteDisable;
		end
endmodule
